import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_lui/flutter_lui.dart';
import 'package:flutter_lui/src/l_style.dart';

///  默认偏移量
final double _totleOffset = lrpx(100);

/// 动画时间
const int _animationTime = 200;

/// 动画出现方向
enum LDialogShowDirection {
  /// 从左往右出现 消失时原路返回
  leftToRight,

  /// 从右往左出现 消失时原路返回
  rightToLeft,

  /// 从上往下出现 消失时原路返回
  topToBottom,

  /// 从下往上出现 消失时原路返回
  bottomToTop,
}

class LDialog {
  static OverlayEntry? _overlayEntry;
  static final double _maskOpacity = LUI.instance.dialogMaskOpacity;

  /// 计数器 相当于一个唯一标识 用于消失动画完成后移除overlayEntry
  static int _count = 1;

  /// 以计数器为Key的所有overlayEntry 集合 关闭时以key移除对应的overlayEntry
  static final Map<int, OverlayEntry?> _entries = {};

  /// 记录不跨页面的dialog是在哪个路由下展示的, \
  /// 当路由有变化时,根据key移除所有路由不一致的弹窗 \
  /// 并显示路由一致的弹窗
  static final Map<int, Route<dynamic>?> _routeMap = {};

  /// 总控制弹窗消失,但不会有动画
  static hide(int key, [bool? isRouteUpdate]) {
    if (_entries[key] != null) {
      _entries[key]!.remove();
      if (isRouteUpdate != true) {
        _entries.remove(key);
        if (_routeMap.containsKey(key) && _routeMap[key] != null) {
          _routeMap.remove(key);
        }
      }
    }
  }

  static handleRouteUpdate(List<Route<dynamic>?> routes) {
    if (_routeMap.keys.isEmpty) return;
    for (int key in _routeMap.keys) {
      if (_routeMap[key] != LNavigatorObserver.currentRoute) {
        hide(key, true);
      } else if (_routeMap[key]!.navigator?.overlay != null &&
          _entries[key] != null) {
        _routeMap[key]!.navigator!.overlay!.insert(_entries[key]!);
      }
    }
  }

  static addListener() {
    if (!LEvents.instance
        .hasListener(LNavigatorObserver.ROUTE_DID_UPDATE, handleRouteUpdate)) {
      LEvents.instance
          .addListener(LNavigatorObserver.ROUTE_DID_UPDATE, handleRouteUpdate);
    }
  }

  /// 基础弹窗显示
  ///
  /// 可以自定义`内容` `显示方向` `显示位置`
  ///
  /// 可以得到一个控制器来控制弹窗消失 并且可以通过`await`得到值
  static showBaseDialog({
    /// 要显示的内容 子组件
    required Widget child,

    /// 回调事件,在页面初始化完成后可以得到一个控制器,用来控制弹窗的消失和返回值
    required Function(LDialogController controller) onCreated,

    /// 背景遮罩层的透明度 0-1
    double? maskOpacity,

    /// 是否禁止背景遮罩层点击后关闭 true=>禁止 点击后不会关闭弹窗
    bool? disabledMask,

    /// 子组件显示在屏幕的位置
    Alignment position = Alignment.center,

    /// 子组件添加到屏幕上时的出现方向
    LDialogShowDirection direction = LDialogShowDirection.bottomToTop,

    /// 是否跨页面展示
    bool? crossPage = true,
  }) async {
    addListener();
    Completer c = Completer();

    LDialog._count++;
    var currCount = LDialog._count;
    _overlayEntry = OverlayEntry(
        builder: (_) => _LDialogBaseWidget(
              maskOpacity: maskOpacity ?? _maskOpacity,
              onCreated: onCreated,
              position: position,
              direction: direction,
              onClose: ([dynamic value]) {
                hide(currCount);
                c.complete(value ?? 0);
              },
              disabledMask: disabledMask ?? false,
              child: child,
            ));
    LDialog._entries[currCount] = _overlayEntry;
    if (LNavigatorObserver.currentRoute?.navigator?.overlay != null) {
      LNavigatorObserver.currentRoute?.navigator!.overlay!
          .insert(_overlayEntry!);
    }
    if (crossPage != true) {
      _routeMap[currCount] = LNavigatorObserver.currentRoute;
    }
    return c.future;
  }

  /// 显示一个确认弹窗
  ///
  /// 可以只设置文字也可以定义内容组件
  ///
  /// 可以设置按钮文字和按钮显示与否 以及确认按钮的颜色
  ///
  /// 最终得到0和1, 0=>点击取消按钮  1=>点击确认按钮
  static Future<dynamic> showConfirmDialog(
    BuildContext context, {
    /// 弹窗标题
    String? title,

    /// 弹窗内容显示的文字
    String? message,

    /// 自定义中间区域的显示内容 优先级高于`message`
    Widget? child,

    /// 自定义构建内容的方法 优先级高于 `child`
    Widget Function(Function onClose)? buildChild,

    /// 确认按钮显示的文本
    String? confirmText,

    /// 取消按钮显示的文本
    String? cancelText,

    /// 确认按钮显示到左侧,取消按钮显示到右侧,但整体还是在右侧
    bool? confirmBtnToLeft,

    /// 遮罩层透明度 0-1
    double? maskOpacity,

    /// 是否禁止点击遮罩层关闭弹窗 true=>点击遮罩层不关闭
    bool? disabledMask,

    /// 是否禁止关闭图标 如果禁止 关闭图标会不显示
    bool? disabledCloseIcon,

    /// 确认按钮背景颜色 文字色始终为白色
    Color? confirmBtnColor,

    /// 是否显示`取消按钮`
    bool? showCancelBtn = true,

    /// 是否显示`确认按钮`
    bool? showConfirmBtn = true,

    /// 是否ios样式 默认为ios样式
    bool? iosStyle = true,

    /// 等待时间 秒 默认为0 等待时间过完才可以关闭 ios样式下才有效
    int? waitingTime,

    /// 是否跨页面展示
    bool? crossPage = true,
  }) async {
    LDialogController? controller;
    return await showBaseDialog(
        disabledMask: disabledMask,
        crossPage: crossPage,
        child: iosStyle == true
            ? _LDialogIosConfirm(
                title: title,
                message: message,
                confirmText: confirmText,
                cancelText: cancelText,
                confirmBtnToLeft: confirmBtnToLeft,
                confirmBtnColor: confirmBtnColor ?? LUI.instance.primaryColor,
                showCancelBtn: showCancelBtn,
                showConfirmBtn: showConfirmBtn,
                maskOpacity: maskOpacity ?? _maskOpacity,
                disabledCloseIcon: disabledCloseIcon == true,
                waitingTime: waitingTime,
                child: buildChild != null
                    ? buildChild((value) {
                        if (controller != null) {
                          controller!.close(value);
                        }
                      })
                    : child,
                onClose: (value) {
                  if (controller != null) {
                    controller!.close(value);
                  }
                },
              )
            : _LDialogConfirm(
                title: title,
                message: message,
                confirmText: confirmText,
                cancelText: cancelText,
                confirmBtnToLeft: confirmBtnToLeft,
                confirmBtnColor: confirmBtnColor ?? LUI.instance.primaryColor,
                showCancelBtn: showCancelBtn,
                showConfirmBtn: showConfirmBtn,
                maskOpacity: maskOpacity ?? _maskOpacity,
                disabledCloseIcon: disabledCloseIcon == true,
                child: buildChild != null
                    ? buildChild((value) {
                        if (controller != null) {
                          controller!.close(value);
                        }
                      })
                    : child,
                onClose: (value) {
                  if (controller != null) {
                    controller!.close(value);
                  }
                },
              ),
        onCreated: (v) => controller = v);
  }

  /// 显示一个底部按钮列表弹窗
  static Future<dynamic> showBottomActionsDialog(
    List<LDialogBottomAction> actions, {
    /// 是否跨页面展示
    bool? crossPage = true,
  }) async {
    LDialogController? controller;

    return await showBaseDialog(
      crossPage: crossPage,
      child: _LDialogBottomActions(
        actions: actions,
        onActionTap: (value) {
          if (controller != null) {
            controller!.close(value);
          }
        },
      ),
      onCreated: (v) => controller = v,
      position: Alignment.bottomCenter,
    );
  }
}

/// 弹窗控制器
///
/// 用于关闭弹窗
class LDialogController {
  /// 关闭弹窗的方法
  Function([dynamic value]) close;

  /// 是否已销毁
  bool isDispose = false;
  LDialogController({required this.close});
}

class _LDialogBaseWidget extends StatefulWidget {
  final double maskOpacity;
  final Widget child;
  final Function([dynamic value]) onClose;
  final bool disabledMask;
  final Function(LDialogController) onCreated;

  final Alignment position;
  final LDialogShowDirection direction;
  const _LDialogBaseWidget(
      {required this.child,
      required this.disabledMask,
      required this.maskOpacity,
      required this.onClose,
      required this.onCreated,
      required this.position,
      required this.direction,
      Key? key})
      : super(key: key);
  @override
  State<StatefulWidget> createState() => __LDialogBaseWidget();
}

class __LDialogBaseWidget extends State<_LDialogBaseWidget>
    with TickerProviderStateMixin {
  late AnimationController opacityController;
  late LDialogController controller;
  @override
  void initState() {
    super.initState();
    opacityController = AnimationController(
        lowerBound: 0,
        upperBound: 1,
        vsync: this,
        duration: const Duration(milliseconds: _animationTime));
    opacityController.addListener(() {
      setState(() {});
    });
    opacityController.forward();
    controller = LDialogController(close: handleClose);
    WidgetsBinding.instance.addPostFrameCallback(handlePostFrameCallback);
  }

  @override
  void didUpdateWidget(covariant _LDialogBaseWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    controller.close = handleClose;
  }

  @override
  void dispose() {
    opacityController.dispose();
    super.dispose();
  }

  handlePostFrameCallback(_) {
    widget.onCreated(controller);
  }

  handleClose([dynamic value]) {
    if (controller.isDispose) {
      return;
    }
    controller.isDispose = true;
    opacityController.reverse();
    Timer(const Duration(milliseconds: _animationTime), () {
      widget.onClose(value);
    });
  }

  handleMaskClose() {
    if (!widget.disabledMask) {
      handleClose(0);
    }
  }

  @override
  Widget build(BuildContext context) {
    double useLeft = 0;
    double useRight = 0;
    double useTop = 0;
    double useBottom = 0;
    double useOffset = _totleOffset * (1 - opacityController.value);
    if (widget.direction == LDialogShowDirection.bottomToTop) {
      useTop = useOffset;
    } else if (widget.direction == LDialogShowDirection.leftToRight) {
      useRight = useOffset;
    } else if (widget.direction == LDialogShowDirection.rightToLeft) {
      useLeft = useOffset;
    } else if (widget.direction == LDialogShowDirection.topToBottom) {
      useBottom = useOffset;
    }
    return GestureDetector(
      onTap: handleMaskClose,
      child: Material(
          color: Colors.black
              .withOpacity(widget.maskOpacity * opacityController.value),
          child: FadeTransition(
            opacity: opacityController,
            child: Stack(
              children: [
                Positioned(
                  left: useLeft != 0 ? useLeft : null,
                  top: useTop != 0 ? useTop : null,
                  right: useRight != 0 ? useRight : null,
                  bottom: useBottom != 0 ? useBottom : null,
                  child: LBox(
                      height: PageSize.height,
                      width: PageSize.width,
                      alignment: widget.position,
                      child:
                          GestureDetector(onTap: () {}, child: widget.child)),
                ),
              ],
            ),
          )),
    );
  }
}

class _LDialogConfirm extends StatelessWidget {
  final String? title;
  final String? message;
  final String? confirmText;
  final String? cancelText;
  final bool? confirmBtnToLeft;
  final Color confirmBtnColor;
  final bool? showCancelBtn;
  final bool? showConfirmBtn;
  final Widget? child;
  final double maskOpacity;
  final bool disabledCloseIcon;
  final Function(int value) onClose;

  const _LDialogConfirm(
      {this.title,
      this.message,
      this.confirmText,
      this.cancelText,
      this.confirmBtnToLeft,
      this.showCancelBtn,
      this.showConfirmBtn,
      this.child,
      required this.disabledCloseIcon,
      required this.maskOpacity,
      required this.confirmBtnColor,
      required this.onClose,
      Key? key})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    var useCancelBtn = LButton(
      height: lrpx(32),
      shape: LButtonShape.round,
      textColor: LUI.instance.textColor,
      backgroundColor: LUI.instance.borderColor,
      text: cancelText ?? '取消',
      onTap: () => onClose(0),
    );
    var useConfirmBtn = LButton(
      height: lrpx(32),
      shape: LButtonShape.round,
      backgroundColor: confirmBtnColor,
      text: confirmText ?? '确定',
      onTap: () => onClose(1),
    );
    List<Widget> actions = [];
    if (showCancelBtn == true) {
      actions.add(useCancelBtn);
    }
    if (showConfirmBtn == true) {
      if (confirmBtnToLeft == true) {
        actions.insert(0, useConfirmBtn);
        if (showCancelBtn == true) {
          actions.insert(1, Container(width: lrpx(10)));
        }
      } else {
        actions.add(Container(width: lrpx(10)));
        actions.add(useConfirmBtn);
      }
    }

    Widget useChild = child ?? LText(message ?? '');

    return LDialogCenterChildBlock(
      children: [
        LDialogCenterChildTitle(
          title: title ?? '',
          onClose: disabledCloseIcon ? null : onClose,
        ),
        LBox(
          padding: [lrpx(10)],
          child: useChild,
        ),
        LDialogCenterChildFooter(actions: actions),
      ],
    );
  }
}

class LDialogBottomAction {
  final String label;
  final Widget? icon;
  final dynamic value;

  LDialogBottomAction({required this.label, required this.value, this.icon});
}

class _LDialogBottomActions extends StatefulWidget {
  final List<LDialogBottomAction> actions;
  final Function(dynamic value) onActionTap;
  const _LDialogBottomActions(
      {required this.actions, required this.onActionTap, Key? key})
      : super(key: key);

  @override
  State<StatefulWidget> createState() => __LDialogBottomActions();
}

class __LDialogBottomActions extends State<_LDialogBottomActions> {
  @override
  Widget build(BuildContext context) {
    return LBox(
      borderRadius: [lrpx(10), lrpx(10), 0, 0],
      mainAxisSize: MainAxisSize.min,
      children: [
        ...widget.actions.map((i) => LBox(
              borderBottom: BorderSide(color: LUI.instance.borderColor),
              child: LButton(
                  text: i.label,
                  icon: i.icon,
                  backgroundColor: Colors.white,
                  textColor: LUI.instance.textColor,
                  onTap: () => widget.onActionTap(i.value)),
            )),
        LBox(height: lrpx(10), backgroundColor: LUI.instance.borderColor),
        LButton(
          text: '取消',
          backgroundColor: Colors.white,
          textColor: LUI.instance.textColor,
          onTap: () => widget.onActionTap(0),
        )
      ],
    );
  }
}

/// 中间内容块
class LDialogCenterChildBlock extends StatelessWidget {
  final double? width;
  final Color? backgroundColor;
  final List<Widget>? children;
  final double? radius;
  const LDialogCenterChildBlock(
      {this.width, this.backgroundColor, this.children, this.radius, Key? key})
      : super(key: key);
  @override
  Widget build(BuildContext context) {
    return LBox(
      width: width ?? PageSize.width - lrpx(40),
      backgroundColor: backgroundColor ?? Colors.white,
      borderRadius: [radius ?? LStyle.borderRadius],
      mainAxisSize: MainAxisSize.min,
      children: children ?? [],
    );
  }
}

class LDialogCenterChildTitle extends StatelessWidget {
  final double? height;
  final String title;
  final double? fontSize;
  final FontWeight? fontWidget;
  final Function? onClose;
  final Color? closeIconColor;
  final bool? borderBottom;
  const LDialogCenterChildTitle(
      {this.height,
      required this.title,
      this.fontSize,
      this.fontWidget,
      this.onClose,
      this.closeIconColor,
      this.borderBottom = true,
      Key? key})
      : super(key: key);
  @override
  Widget build(BuildContext context) {
    return LBox(
      height: height ?? lrpx(40),
      borderBottom: borderBottom == true
          ? BorderSide(color: LUI.instance.borderColor)
          : null,
      direction: BoxDirections.horizontal,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        LBox(
          marginLeft: lrpx(40),
          flex: 1,
          textAlign: TextAlign.center,
          child: title,
          fontSize: fontSize ?? LStyle.fontSize + lrpx(2),
          fontWeight: fontWidget ?? FontWeight.w600,
        ),
        LBox(
          width: lrpx(40),
          height: lrpx(40),
          backgroundColor: Colors.transparent,
          onTap: onClose == null ? null : () => onClose!(0),
          alignment: Alignment.center,
          child: Icon(
            Icons.close,
            size: lrpx(20),
            color: onClose == null
                ? Colors.transparent
                : closeIconColor ?? const Color(0xff666666),
          ),
        ),
      ],
    );
  }
}

class LDialogCenterChildFooter extends StatelessWidget {
  final List<Widget>? actions;
  final MainAxisAlignment? mainAxisAlignment;
  const LDialogCenterChildFooter(
      {this.actions, this.mainAxisAlignment, Key? key})
      : super(key: key);
  @override
  Widget build(BuildContext context) {
    return LBox(
        padding: [lrpx(10)],
        borderTop: BorderSide(color: LUI.instance.borderColor),
        direction: BoxDirections.horizontal,
        mainAxisAlignment: mainAxisAlignment ?? MainAxisAlignment.end,
        children: actions);
  }
}

class _LDialogIosConfirm extends StatefulWidget {
  final String? title;
  final String? message;
  final String? confirmText;
  final String? cancelText;
  final bool? confirmBtnToLeft;
  final Color confirmBtnColor;
  final bool? showCancelBtn;
  final bool? showConfirmBtn;
  final Widget? child;
  final double maskOpacity;
  final bool disabledCloseIcon;
  final int? waitingTime;
  final Function(int value) onClose;

  const _LDialogIosConfirm(
      {this.title,
      this.message,
      this.confirmText,
      this.cancelText,
      this.confirmBtnToLeft,
      this.showCancelBtn,
      this.showConfirmBtn,
      this.child,
      this.waitingTime,
      required this.disabledCloseIcon,
      required this.maskOpacity,
      required this.confirmBtnColor,
      required this.onClose,
      Key? key})
      : super(key: key);

  @override
  State<StatefulWidget> createState() => __LDialogIosConfirm();
}

class __LDialogIosConfirm extends State<_LDialogIosConfirm> {
  int waitingTime = 0;
  Timer? timer;
  @override
  void initState() {
    super.initState();
    if (widget.waitingTime != null) {
      waitingTime = widget.waitingTime!;
      startTimer();
    }
  }

  @override
  void dispose() {
    super.dispose();
    timer?.cancel();
    timer = null;
  }

  startTimer() {
    timer = Timer.periodic(const Duration(seconds: 1), (t) {
      setState(() {
        waitingTime--;
        if (waitingTime <= 0) {
          timer!.cancel();
          timer = null;
        }
      });
    });
  }

  handleClose(int t) {
    if (waitingTime > 0) return;
    widget.onClose(t);
  }

  @override
  Widget build(BuildContext context) {
    final bottomHeight = rpx(40);
    var useCancelBtn = LBox(
      flex: 1,
      height: bottomHeight,
      color: waitingTime > 0 ? LUI.instance.color9 : LUI.instance.textColor,
      child: widget.cancelText ?? '取消',
      alignment: Alignment.center,
      onTap: () => handleClose(0),
    );
    var useConfirmBtn = LBox(
      flex: 1,
      height: bottomHeight,
      color: waitingTime > 0 ? LUI.instance.color9 : widget.confirmBtnColor,
      child: (widget.confirmText ?? '确定') +
          (waitingTime > 0 ? '($waitingTime)s' : ''),
      alignment: Alignment.center,
      onTap: () => handleClose(1),
    );

    List<Widget> actions = [];
    if (widget.showCancelBtn == true) {
      actions.add(useCancelBtn);
    }
    if (widget.showConfirmBtn == true) {
      if (widget.confirmBtnToLeft == true) {
        actions.insert(0, useConfirmBtn);
        if (widget.showCancelBtn == true) {
          actions.insert(
              1,
              LBox(
                  width: rpx(1),
                  height: bottomHeight,
                  backgroundColor: LUI.instance.borderColor));
        }
      } else {
        actions.add(LBox(
            width: rpx(1),
            height: bottomHeight,
            backgroundColor: LUI.instance.borderColor));
        actions.add(useConfirmBtn);
      }
    }

    Widget useChild = LBox(
        paddingBottom: rpx(20),
        child: widget.child ?? LText(widget.message ?? ''));

    return LDialogCenterChildBlock(
      radius: rpx(10),
      children: [
        LDialogCenterChildTitle(
          title: widget.title ?? '',
          onClose: widget.disabledCloseIcon ? null : handleClose,
          borderBottom: false,
        ),
        LBox(
          padding: [rpx(10)],
          child: useChild,
        ),
        LBox(
            borderTop: BorderSide(color: LUI.instance.borderColor),
            direction: BoxDirections.horizontal,
            children: actions)
      ],
    );
  }
}
